/* 
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Contributor(s):
 *   Lan Qiang <jameslan@gmail.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK *****
 */

#include <stdio.h>
#include <stdlib.h>

#define MOZILLA_INTERNAL_API
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsIZipFileIO.h"
#include "nsIZipIOQueue.h"
#include "nsIServiceManager.h"
#include "nsILocalFile.h"
#include "nsCRT.h"
#include "nsString.h"
#include "nsIStringEnumerator.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsVoidArray.h"
#include "nsStringEnumerator.h"

#include <set>
#include <vector>
using namespace std;

static const char gZipFileIOContractID[] = "org.mozilla@zipfileio;1";
static const char gZipIOQueueContractID[] = "org.mozilla@zipioqueue;1";

#define Mozip_Existing_Archive "/Users/flyingboy/mozip.zip"
#define Mozip_New_Archive "/Users/flyingboy/new.zip"

nsresult Test_ZipFileIO_Open()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));

	NS_ENSURE_TRUE(zipio, NS_ERROR_OUT_OF_MEMORY);

	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));

	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Close();
	NS_ENSURE_SUCCESS(rv, rv);

	return NS_OK;
}

nsresult Test_ZipFileIO_HasEntry()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));

	rv = zipio->Open(file);

	PRBool exist;
	rv = zipio->HasEntry(NS_LITERAL_CSTRING("nsZipIOImpl.cpp"), &exist);
	NS_ENSURE_SUCCESS(rv, rv);
	NS_ASSERTION(exist, "nsZipIOImpl.cpp should exist");

	rv = zipio->HasEntry(NS_LITERAL_CSTRING("zzip/"), &exist);
	NS_ENSURE_SUCCESS(rv, rv);
	NS_ASSERTION(exist, "zzip should exist");

	rv = zipio->Close();

	return NS_OK;
}

nsresult Test_ZipFileIO_GetEntry()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));

	rv = zipio->Open(file);

	nsCOMPtr<nsIZipEntry> entry;
	rv = zipio->GetEntry("nsZipIOImpl.cpp", getter_AddRefs(entry));
	NS_ENSURE_SUCCESS(rv, rv);
	NS_ASSERTION(entry, "nsZipIOImpl.cpp have not got");

	rv = zipio->GetEntry("zzip/", getter_AddRefs(entry));
	NS_ENSURE_SUCCESS(rv, rv);
	NS_ASSERTION(entry, "zzip/ have not got");

	rv = zipio->Close();

	return NS_OK;
}

nsresult ZipFileIO_FindEntries_TestCase(const char* lookfor, const set<nsCAutoString> &expected)
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	set<nsCAutoString> dup(expected);
	nsCOMPtr<nsIUTF8StringEnumerator> enumerator;
	rv = zipio->FindEntries(lookfor, getter_AddRefs(enumerator));
	NS_ENSURE_SUCCESS(rv, rv);
	for (;;) {
		PRBool more;
		rv = enumerator->HasMore(&more);
		NS_ENSURE_SUCCESS(rv, rv);
		if (!more) {
			break;
		}
		nsCAutoString entry;
		rv = enumerator->GetNext(entry);
		NS_ENSURE_SUCCESS(rv, rv);
		// remove found entry from expected entries set
		NS_ASSERTION(dup.count(entry), entry.get());
		dup.erase(entry);
	}

	NS_ASSERTION(dup.empty(), "Do not find all entries");

	rv = zipio->Close();

	return NS_OK;
}

nsresult Test_ZipFileIO_FindEntries()
{
	char * pattern1 = "ns*";
	nsCAutoString files1[] = {nsCAutoString("nsZipIOImpl.cpp"), nsCAutoString("nsZipIOImpl.h"), nsCAutoString("nsZipIOQueue.h")};
	set<nsCAutoString> set1(files1, files1+3);

	nsresult rv;
	rv = ZipFileIO_FindEntries_TestCase(pattern1, set1);
	NS_ENSURE_SUCCESS(rv, rv);

	char * pattern2 = "zzip*";
	nsCAutoString files2[] = {nsCAutoString("zzip/"), nsCAutoString("zzip/lisa/"), nsCAutoString("zzip/lisa/nsIZipFileIO.idl"),
		nsCAutoString("zzip/nsIZipIOQueue.idl"), nsCAutoString("zzip/z/"), nsCAutoString("zzip/z/x/")};
	set<nsCAutoString> set2(files2, files2+6);

	rv = ZipFileIO_FindEntries_TestCase(pattern2, set2);
	NS_ENSURE_SUCCESS(rv, rv);
	return NS_OK;
}

nsresult Test_ZipFileIO_Extract()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	NS_ENSURE_TRUE(zipio, NS_ERROR_OUT_OF_MEMORY);

	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	nsCOMPtr<nsILocalFile> zipfile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	rv = zipfile->InitWithPath(NS_LITERAL_STRING("/Users/flyingboy/nsIZipIOQueue.idl"));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Extract("zzip/nsIZipIOQueue.idl", zipfile);
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipfile->InitWithPath(NS_LITERAL_STRING("/Users/flyingboy/x"));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Extract("zzip/z/", zipfile);
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Close();
	NS_ENSURE_SUCCESS(rv, rv);

	return NS_OK;
}

nsresult Test_ZipFileIO_Test()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));

	NS_ENSURE_TRUE(zipio, NS_ERROR_OUT_OF_MEMORY);

	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));

	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Test("zzip/lisa/nsIZipFileIO.idl");
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Test(nsnull);
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Close();
	NS_ENSURE_SUCCESS(rv, rv);

	return NS_OK;
}

nsresult Test_ZipFileIO_InputStream()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	NS_ENSURE_TRUE(zipio, NS_ERROR_OUT_OF_MEMORY);

	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	nsCOMPtr<nsIInputStream> is;
	rv = zipio->GetInputStream("zzip/lisa/nsIZipFileIO.idl", getter_AddRefs(is));
	NS_ENSURE_SUCCESS(rv, rv);

	nsCOMPtr<nsILocalFile> out(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(out, NS_ERROR_OUT_OF_MEMORY);

	rv = out->InitWithPath(NS_LITERAL_STRING("/Users/flyingboy/nsIZipFileIO.idl"));
	NS_ENSURE_SUCCESS(rv, rv);

	PRFileDesc* fd;
	rv = out->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644, &fd);
	NS_ENSURE_SUCCESS(rv, rv);

	PRUint32 bytes;
	char buf[65536];

	for (;;) {
		rv = is->Available(&bytes);
		NS_ENSURE_SUCCESS(rv, rv);

		if (!bytes) break;

		rv = is->Read(buf, 65536, &bytes);
		NS_ENSURE_SUCCESS(rv, rv);

		if (PR_Write(fd, buf, bytes) < 0) {
			PR_Close(fd);
			return NS_ERROR_FILE_DISK_FULL;
		}
	}

	PR_Close(fd);
	return NS_OK;
}

nsresult Test_ZipFileIO_RemoveEntries()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	NS_ENSURE_TRUE(zipio, NS_ERROR_OUT_OF_MEMORY);

	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	nsCStringArray toRemove;
	toRemove.AppendCString(NS_LITERAL_CSTRING("nsZipIOImpl.cpp"));
	toRemove.AppendCString(NS_LITERAL_CSTRING("nsZipIOImpl.h"));

	nsCOMPtr<nsIUTF8StringEnumerator> enumerator;
	rv = NS_NewUTF8StringEnumerator(getter_AddRefs(enumerator), &toRemove);
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->RemoveEntries(enumerator);
	NS_ENSURE_SUCCESS(rv, rv);

	return NS_OK;
}

nsresult Test_ZipFileIO_AddEntryFromFile()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	NS_ENSURE_TRUE(zipio, NS_ERROR_OUT_OF_MEMORY);

	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));

	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	// a file
	nsCOMPtr<nsILocalFile> file1(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(file1, NS_ERROR_OUT_OF_MEMORY);

	rv = file1->InitWithPath(NS_LITERAL_STRING("/Users/flyingboy/missfont.log"));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->AddEntryFromFile("missfont.log", file1, nsIZipFileIO::ZipLevelDefault);
	NS_ENSURE_SUCCESS(rv, rv);

	// a dir with '/'
	nsCOMPtr<nsILocalFile> file2(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(file2, NS_ERROR_OUT_OF_MEMORY);

	rv = file2->InitWithPath(NS_LITERAL_STRING("/Users/flyingboy/libzip/"));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->AddEntryFromFile("lib/", file2, nsIZipFileIO::ZipLevelMax);
	NS_ENSURE_SUCCESS(rv, rv);

	// a dir without '/'
	nsCOMPtr<nsILocalFile> file3(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(file3, NS_ERROR_OUT_OF_MEMORY);

	rv = file3->InitWithPath(NS_LITERAL_STRING("/Users/flyingboy/libzip/"));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->AddEntryFromFile("333", file3, nsIZipFileIO::ZipLevelSpeed);
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Close();
	NS_ENSURE_SUCCESS(rv, rv);

	return NS_OK;
}

nsresult Test_ZipFileIO_AddEntry()
{
	nsCOMPtr<nsIZipFileIO> zipio(do_CreateInstance(gZipFileIOContractID));
	NS_ENSURE_TRUE(zipio, NS_ERROR_OUT_OF_MEMORY);

	nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));

	NS_ENSURE_TRUE(file, NS_ERROR_OUT_OF_MEMORY);

	nsresult rv;
	rv = file->InitWithPath(NS_LITERAL_STRING(Mozip_Existing_Archive));
	NS_ENSURE_SUCCESS(rv, rv);

	rv = zipio->Open(file);
	NS_ENSURE_SUCCESS(rv, rv);

	// a file
	nsCOMPtr<nsIOutputStream> os;
	rv = zipio->AddEntry("Consola.ttf", nsIZipFileIO::ZipLevelMax, getter_AddRefs(os));
	NS_ENSURE_SUCCESS(rv, rv);

	nsCOMPtr<nsILocalFile> in(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
	NS_ENSURE_TRUE(in, NS_ERROR_OUT_OF_MEMORY);

	rv = in->InitWithPath(NS_LITERAL_STRING("/Users/flyingboy/Consola.ttf"));
	NS_ENSURE_SUCCESS(rv, rv);

	PRFileDesc* fd;
	rv = in->OpenNSPRFileDesc(PR_RDONLY, 0000, &fd);
	NS_ENSURE_SUCCESS(rv, rv);

	char buf[16384];

	for (;;) {
		PRInt32 bytes = PR_Read(fd, buf, 16384);
		if (bytes <= 0) break;

		PRUint32 bytesWritten;
		rv = os->Write(buf, bytes, &bytesWritten);
		NS_ENSURE_SUCCESS(rv, rv);
	}
	PR_Close(fd);
	printf("closing\n");
	os->Close();

	printf("testing\n");
	rv = zipio->Test("Consola.ttf");
	NS_ENSURE_SUCCESS(rv, rv);

	return NS_OK;
}

nsresult Test_ZipFileIO()
{
	nsresult rv;

	//rv = Test_ZipFileIO_Open();

	rv = Test_ZipFileIO_HasEntry();

	rv = Test_ZipFileIO_GetEntry();

	rv = Test_ZipFileIO_FindEntries();

	rv = Test_ZipFileIO_Test();

	rv = Test_ZipFileIO_Extract();

	rv = Test_ZipFileIO_InputStream();

	rv = Test_ZipFileIO_RemoveEntries();

	rv = Test_ZipFileIO_AddEntryFromFile();

	rv = Test_ZipFileIO_AddEntry();

	return NS_OK;
}

class QueueEventHandler : nsIZipIOQueueEventSink
{
public:
	NS_DECL_ISUPPORTS
	NS_DECL_NSIZIPIOQUEUEEVENTSINK
};

nsresult QueueEventHandler::OnActionBegin()
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

nsresult QueueEventHandler::OnActionEnd()
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

nsresult QueueEventHandler::OnFinish()
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

nsresult Test_ZipIOQueue()
{
	nsresult rv;

	return NS_OK;
}

nsresult TestMozip()
{
	nsresult rv;

	rv = Test_ZipFileIO();

	rv = Test_ZipIOQueue();

	return NS_OK;
}

int main()
{
	nsresult rv;
        rv = NS_InitXPCOM2(nsnull, nsnull, nsnull);
	if (NS_FAILED(rv)) {
		printf("NS_InitXPCOM failed!\n");
		return 1;
	}

	rv = TestMozip();
	if (NS_FAILED(rv)) {
		printf("Testing failed!\n");
		return 1;
	}

	rv = NS_ShutdownXPCOM(nsnull);
	if (NS_FAILED(rv)) {
		printf("NS_ShutdownXPCOM failed!\n");
		return 1;
	}

	return 0;
}
